今天大概會聊到的範圍
- viewModel in Compose
今天的主題很單純:如果專案中有使用到 Compose 又有用到 Android Architecture Component 的 ViewModel 與 LiveData 的話,要怎麼讓資料在 Compose 內外傳遞呢?
首先,先建立今天的主角 ViewModel
class FooViewModel : ViewModel() {
    
    val buzzLiveData: LiveData<List<String>>
        get() = _buzzLiveData
    
    private val _buzzLiveData = MutableLiveData<List<String>>(emptyList())
    
    fun addData(param: String) {
        _buzzLiveData.value = _buzzLiveData.value?.let { it + listOf(param) }
    }
}
ViewModel 可以在 Activity 層建立,方法就和一般的 ViewModel 一樣:
class BarActivity : ComponentActivity() {
    
    private val vm: FooViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            FooComposable(vm) // <-- 
        }
    }
}
@Composable
fun FooComposable(vm: FooViewModel) { ... }
在 Activity 建立 ViewModel 後,可以將其當成一般的參數傳入 Composable。
@Composable
fun FooComposable(model: FooViewModel) {
    
    val data by model.buzzLiveData.observeAsState(initial = emptyList())  // <--- 1
    
    Column {
        LazyColumn {
            items(data) {        // <--- 2
                Text(it)
            }
        }
        
        Button({ model.addData("one") }) {    // <--- 3
            Text("add data")
        }
    }
}
observeAsState需要implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
observeAsState 來將 LiveData 轉換成 State注意!呼叫 ViewModel function 的部分若是放在 composable function 中,會變成 side-effect 。若真的要執行需要特別處理 ( 上一篇的主題 )
Button 的 onClick callback 不是 composable function
@Composable
fun FooComposable(model: FooViewModel = viewModel()) { ... }
若不一定要外部的使用者提供 ViewModel,也可以透過 viewModel() 來現場建立一個 viewModel。
observeAsState Under the hoodobserveAsState 到底怎麼把 LiveData 轉成 State 的呢?我們可以來研究一下 observeAsState 的實作:
@Composable
fun <R, T : R> LiveData<T>.observeAsState(initial: R): State<R> {
    val lifecycleOwner = LocalLifecycleOwner.current      // < --- 1
    val state = remember { mutableStateOf(initial) }      // < --- 2
    DisposableEffect(this, lifecycleOwner) {                           // < --- 5
        val observer = Observer<T> { state.value = it }   // < --- 3
        observe(lifecycleOwner, observer)                
        onDispose { removeObserver(observer) }                         // < --- 6
    }
    return state                                          // < --- 4
}
LocalLifecycleOwner 可以取得目前 Composable 所在 scope 的 LifecycleOwner
remember { mutableStateOf() } 將資料存放起來LiveData.observe 去觀察 LiveData 的資料,若有改動就將資料同步到 stateLiveData or LifecycleOwner 有異動的時候能重新做 observe ,且除此之外不要做異動。使用之前提到過的 DisposableEffect
onDispose 時,將 observer 回收想不到 ViewModel 和 LiveData 在 Compose 中的使用其實非常的簡單。在 LiveData 背後,又剛好是使用到之前所研究的 Side Effect API,感覺整個技術都串起來了!
Reference: